
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections;
using System.Text;

using Swaf;
using Swaf.Config;

namespace Swaf.BizObj
{
	internal class UnknownReferences
	{
		protected IDictionary m_unkownTypes = new Hashtable();
		public void fixUnknownReferences(BizObjManager mgr, BizObjDefinition def)
		{
			UnknownType unknown = m_unkownTypes[def.Name] as UnknownType;
			if (unknown != null)
			{
				foreach (IUnknownReference reference in unknown.References)
					reference.fixReferences(mgr, def);
				m_unkownTypes.Remove(def.Name);
			}
		}

		public int Count { get { return m_unkownTypes.Count; } }

		public UnknownType addUnknownTypeReference(string typeName, string bizObjDef, string bizObjField,
			IConfigElement location, string fileName)
		{
			UnknownType typeRef = m_unkownTypes[typeName] as UnknownType;
			if (typeRef == null)
				m_unkownTypes[typeName] = typeRef = new UnknownType(typeName, bizObjDef, bizObjField, fileName, location.LineNumber);
			else
				typeRef.addReference(bizObjDef, bizObjField, fileName, location.LineNumber);

			return typeRef;
		}

		public UnknownType addUnknownTypeReference(string typeName, IBizObjDefinition inject,
			IConfigElement location, string fileName)
		{
			UnknownType typeRef = m_unkownTypes[typeName] as UnknownType;
			if (typeRef == null)
				m_unkownTypes[typeName] = typeRef = new UnknownType(typeName, inject, fileName, location.LineNumber);
			else
				typeRef.addReference(inject, fileName, location.LineNumber);

			return typeRef;
		}

		public string GetUnknownErrorInfo()
		{
			StringBuilder buff = new StringBuilder("\n");
			foreach (UnknownType unknownType in m_unkownTypes.Values)
				unknownType.addError(buff);
			return buff.ToString();
		}
	}

	/// <summary>
	/// This class is only used during startup and maintains information
	/// about a reference to a type not defined at a point of reference.
	/// </summary>
    internal class UnknownType
	{
		public string TypeName;
		public IList References = new ArrayList();
		public UnknownType(string typeName, string referringBizObj, string referringField, string fileName, int lineNum)
		{
			TypeName = typeName;
			References.Add(new UnknownTypeInBizObj(referringBizObj, referringField, fileName, lineNum));
		}
		public UnknownType(string typeName, IBizObjDefinition injectionDef, string fileName, int lineNum)
		{
			TypeName = typeName;
			References.Add(new UnknownTypeInjection(injectionDef, fileName, lineNum));
		}

		public void addReference(string referringBizObj, string referringField, string fileName, int lineNum)
		{
			References.Add(new UnknownTypeInBizObj(referringBizObj, referringField, fileName, lineNum));
		}
		public void addReference(IBizObjDefinition injectionDef, string fileName, int lineNum)
		{
			References.Add(new UnknownTypeInjection(injectionDef, fileName, lineNum));
		}
		public void addError(StringBuilder buff)
		{
			buff.Append("Unknown type ").Append(TypeName).Append(" is referenced by the following:\n");
			foreach (IUnknownReference unknownRef in References)
				unknownRef.addError(buff);

		}
	}

	internal interface IUnknownReference
	{
		void fixReferences(BizObjManager mgr, BizObjDefinition referencedType);
		void addError(StringBuilder buff);
	}

	internal class UnknownTypeInBizObj : IUnknownReference
	{
		public string ReferringBizObj;
		public string ReferringField;
		public string FileName;
		public int LineNum;
		public UnknownTypeInBizObj(string refBizObj, string refField, string fileName, int lineNum)
		{
			ReferringBizObj = refBizObj;
			ReferringField = refField;
			FileName = fileName;
			LineNum = lineNum;
		}

		public void fixReferences(BizObjManager mgr, BizObjDefinition referencedType)
		{
			BizObjDefinition referringType = mgr.getDefinition(ReferringBizObj) as BizObjDefinition;
			if (ReferringField == null)
			{
				//We had a bizobj definition that extended another but at the time it was not know.
				referringType.setParent(referencedType);
			}
			else
			{
				//We had a field in a bizobj definition for a type that wasnt known at the time.
				BizObjField field = referringType.getField(ReferringField, true) as BizObjField;
				switch (field.BizType)
				{
					case BizFieldType.BizObj:
						((BaseBizObj)field.SampleValue).setType(referencedType);
						break;
					case BizFieldType.BizObjList:
						((BaseBizObjList)field.SampleValue).setType(referencedType);
						break;
					default:
						throw new BaseException(String.Format("Bad situation in fixReferences. BizObjField should be a BizObj or BizObj list but is {0}",field.BizType));
						break;
				}
			}
		}

		public void addError(StringBuilder buff)
		{
			if (ReferringField != null)
				buff.Append("	Bizobj field '").Append(ReferringBizObj)
					.Append(".").Append(ReferringField).Append("' in config file ")
					.Append(FileName).Append(" on line ").Append(LineNum).Append("\n");
			else
				buff.Append("	Bizobj '").Append(ReferringBizObj)
					.Append("' extends it in config file ").Append(FileName)
					.Append(" on line ").Append(LineNum).Append("\n");
		}
	}

	internal class UnknownTypeInjection : IUnknownReference
	{
		public IBizObjDefinition InjectionDefinition;
		public string FileName;
		public int LineNum;
		public UnknownTypeInjection(IBizObjDefinition injectionDef, string fileName, int lineNum)
		{
			InjectionDefinition = injectionDef;
			FileName = fileName;
			LineNum = lineNum;
		}

		public void fixReferences(BizObjManager mgr, BizObjDefinition referencedType)
		{
			//Intentionally left blank.  There isn't anything to do as long as the real bizobj
			//definition was found.  This instance was created because a bizobj definition was
			//injecting fields into a bizobj definition that wasn't defined.  If this method is
			//called, the bizobj definition was defined.  However, it will already
			//have modified this.InjectionDefinition so there isnt anything to do.  However, if
			//at the end of startup this record is still in the unknown types list, that means the
			//bizobj definition being injected into was never found and should be displayed as an
			//error.
		}

		public void addError(StringBuilder buff)
		{
			buff.Append("	Bizobj '").Append(InjectionDefinition.Name).Append("' was injected into in config file ")
				.Append(FileName).Append(" on line ").Append(LineNum).Append("\n");
		}
	}
}
